package work.base.jvm.class_eg;


import cn.hutool.core.convert.Convert;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Class类的使用
 * Field.get() 动态获取字段的值
 * Method.invoke() 动态执行方法
 */
public class ClassTest02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ClassEntity entity1 = new ClassEntity();
        ClassEntity entity2 = new ClassEntity("SML");

        // 1 创建class对象的三种方法
        // Class类是泛型类
        Class<ClassEntity> class1 = ClassEntity.class;
        // Class.forName() 需要使用类的全名作为参数
        Class class2 = Class.forName("work.base.jvm.class_eg.ClassEntity");
        Class class3 = entity1.getClass();


        // 2 通过class对象获取到Field然后得到实体对象的指定字段的值  public修饰的字段才能得到
        // Field类的使用 可以修改字段的值 还可以获取到字段的值 参数一般是实体对象
        Field field= class1.getDeclaredField("name");
        String name1 = (String) field.get(entity2);
        field.set(entity2,"SML123");
        //String name2 = (String) field.get(null) ;
        System.out.println(name1);
        System.out.println((String) field.get(entity2));
        // Field.get 如果Field是static字段 参数是null也可以 不是static字段 get()的参数必须是实体对象 否则会报错
        Field staticField = class1.getDeclaredField("staticValue");
        String b= (String)staticField.get("") ;
        String d= (String)staticField.get(null) ;
        System.out.println(b);
        System.out.println(d);


        // 3 通过class对象的newInstance创建实体对象 返回结果一般是Object类型的
        //但是如果class对象定义了泛型的类型 可以直接返回实体对象的类型
        Object obj = class2.getDeclaredConstructor().newInstance();
//        ClassEntity newEntity1 = Convert.convert(ClassEntity.class,obj);
//        ClassEntity newEntity = class1.getDeclaredConstructor().newInstance();


        // 4  Method类的使用
        // invoke()方法的参数是实体对象和指定的方法的实际参数值
        // 执行某个方法必然是某个确定的对象的方法 静态方法调用的invoke的第一个参数可以是class对象 而不是实际的实体对象
        // Method类的invoke的第二个参数是object类型的数组 可以包含多个参数
        // invoke返回的也是Object类型 一般用类型的强制转化 推荐hutool的Convert类 指定class对象
        // 如果调用的方法本身没有返回值 invoke的返回也是null
        // getDeclaredMethod获取指定的Method对象时, 如果方法是有参数的, 需要指定参数类型
        Method m1 = class1.getDeclaredMethod("outInfo");
        Method m2 = class1.getDeclaredMethod("getName");
        Method m3 = class1.getDeclaredMethod("setName", String.class);
        Method m4 = class1.getDeclaredMethod("staticHi");
        m1.invoke(entity1);
        entity1.setName("Method Name");
        Object methodObj = m2.invoke(entity1);
        System.out.println(methodObj);
        // 调用setName() 方法改变name的值
        m3.invoke(entity1,"重新设置msg信息！");
        System.out.println(entity1);
        // 调用静态方法
        m4.invoke(class1);

        System.out.println("End");

    }
}
